"""
# nat-traversal

- udp打洞, 实现p2p通信

"""

import socket
import threading
import time
import sys
import tools
import json
from bdtime import tt


CODING_TYPE = 'utf-8'
peer_ip = peer_port = ""
host_dc = {}
port = None
HEARTBEAT_INTERVAL = 3


def heartbeat():
    while True:
        # msg = '%d|%s|%s' % (9,"keep ","live")
        msg = f'{9}|p{port}_{tt.now(2)}|{"keep_live"}'
        udpSerSock.sendto(msg.encode(CODING_TYPE), server_addr)
        time.sleep(HEARTBEAT_INTERVAL)


def revMsg():
    global peer_ip,peer_port, host_dc
    print(" start rev:")
    while True:
        data, addr = udpSerSock.recvfrom(1024)
        data = data.decode(CODING_TYPE)

        try:
            (msg_type,msg_arg1,msg_arg2)=str(data).split('|')
        except:
            print('[error msg!]', str(data))
            continue
        if int(msg_type)==9:
            continue
        elif int(msg_type)==0:
            print('[login success: %s]' % (str(data)))
        elif int(msg_type)==1:
            print("online hosts:\n%s"% (msg_arg1))
            host_dc = msg_arg1
            print('\n\n ======== \n', host_dc)
            host_dc = host_dc.replace("'", '"')
            host_dc = json.loads(host_dc)
            # host_dc = json.loads(json.dumps(host_dc))
            print(type(host_dc), host_dc)
            # print( json.loads(host_ls))
            print('~~~~~~~~~')
        elif int(msg_type)==2:
            print('[peer online] --- recv:', (msg_type,msg_arg1,msg_arg2))
            peer_ip=msg_arg1
            peer_port=msg_arg2
        elif int(msg_type)==3:
            print('[peer offline]')
        elif int(msg_type)==4:
            print('[respond peer]')
            msg = '%d|%s|%s' % (9,"hello"," ")
            udpSerSock.sendto(msg.encode(CODING_TYPE), (msg_arg1,int(msg_arg2)))
        elif int(msg_type)==5:
            print('[peer msg]:%s'% msg_arg1)
        elif int(msg_type) == -1:
            print(f'*** p{port} will close!')
            udpSerSock.close()
            return
        else:
            print('[error Type!]', str(data))


if __name__ == '__main__':
    # if sys.argv.__len__() == 1:
    #     # 房间名
    #     room_id = 9789
    #     print(f'未指定room_id, 将默认设置为{room_id}!')
    # else:
    #     room_id = sys.argv[1]

    # server_addr = ('192.168.0.103', 6000)
    # server_addr = ('172.19.25.240', 6000)
    host_name = 'www.bodexiong.vip'
    host_ip = socket.gethostbyname(host_name)
    print(f'host_ip: {host_ip}')
    server_addr = (host_ip, 8022)

    udpSerSock= socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # windows上需要绑定端口, 不然报错
    port = tools.get_a_not_in_use_port()
    # port = 8022
    # client_addr = ('127.0.0.1',port)
    client_addr = (tools.get_host_ip(), port)
    # client_addr = ('',port)

    udpSerSock.bind(client_addr)

    print(f'server_addr, client_addr: {server_addr}, {client_addr}')

    Clinet_Name="clinet_A"

    try:
        rev_thread = threading.Thread(target=revMsg)
        rev_thread.setDaemon(True)
        rev_thread.start()

        heat_thread=threading.Thread(target = heartbeat)
        heat_thread.setDaemon(True)
        heat_thread.start()
    except:
        print("Error: unable to start thread")

    # ======================== 自动连接
    host_name = f'p{port}'
    udpSerSock.sendto(('%d|%s|%s' % (0,str(host_name).strip()," ")).encode(CODING_TYPE), server_addr)

    # 依次执行0，1，2，3命令后完成p2p连接并可以直接聊天
    while True:
        comd = input("command[0: connect, 1: get_host_dc, 2: connect_by_name , 3: send_msg, 4: reset_host_dc]\n>>")

        # print(f'command --- {comd}, {type(comd)}')
        # query online host
        if str(comd).strip()=='0':
            # host_name = input("hostname>>")
            # host_name = f'p{port}'
            udpSerSock.sendto(('%d|%s|%s' % (0,str(host_name).strip()," ")).encode(CODING_TYPE), server_addr)
        elif str(comd).strip()=='1':
            print("get online host...")
            udpSerSock.sendto(('%d|%s|%s' % (1," "," ")).encode(CODING_TYPE), server_addr)
        elif str(comd).strip()=='2':
            peer_name = input("peer_name>>")
            print("get peer ip...")
            udpSerSock.sendto(('%d|%s|%s' % (2,str(peer_name).strip()," ")).encode(CODING_TYPE), server_addr)
        elif str(comd).strip()=='3':
            msg = input("Msg>>")
            udpSerSock.sendto(('%d|%s|%s' % (5,str(msg).strip()," ")).encode(CODING_TYPE), (peer_ip,int(peer_port)))
        elif str(comd).strip() == '4':
            # 清理host_ls
            msg = f'4||'
            udpSerSock.sendto(msg.encode(CODING_TYPE), server_addr)
        elif str(comd).strip() == '5':
            # 连接第一个
            # host_dc: dict
            peer_name = ''
            for k, v in host_dc.items():
                k: str
                if not k.endswith(str(port)):
                    peer_name = k
                    print(f'--- peer_name, peer_ip: {k}, {v}')
            udpSerSock.sendto(('%d|%s|%s' % (2,str(peer_name).strip()," ")).encode(CODING_TYPE), server_addr)
        elif not comd:
            pass
        else:
            msg = '变量[comd]取值错误!'
            print(msg)
            # raise ValueError(msg)

        if str(comd).strip()=='exit':
            print("exit!")
            break
    udpSerSock.close()

